package com.qipay.security.jwt.filter;

import com.qipay.security.jwt.service.JWTTokenService;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    Logger logger = LogManager.getLogger(JwtAuthenticationTokenFilter.class);

    @Autowired
    private UserDetailsService jwtUserDetailsService;

    @Autowired
    JWTTokenService jwtTokenService;

    @Value("${jwt.header}")
    private String tokenHeader;

    @Value("${jwt.token.head}")
    private String tokenHead;

    @Value("${jwt.http.key}")
    private String tokenHttpParamKey;

    @Override
    protected void doFilterInternal
            (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain)
            throws ServletException, IOException {

        final String authToken = getToken(httpServletRequest);
        if(StringUtils.isNotBlank(authToken)) {
            String username = jwtTokenService.getUsernameFromToken(authToken);

            logger.info("checking authentication " + username);

            if (StringUtils.isNotBlank(username) && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
                if (jwtTokenService.validateToken(authToken, userDetails)) {
                    UsernamePasswordAuthenticationToken authenticationToken =
                            new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                    logger.info("authenticated user " + username + ", setting security context");
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
        }

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    private String getToken(HttpServletRequest httpServletRequest) {
        String token = getTokenFromHttpHead(httpServletRequest);
        if (StringUtils.isBlank(token)) {
            token = getTokenFromHttpParams(httpServletRequest);
        }
        return token;
    }

    private String getTokenFromHttpParams(HttpServletRequest httpServletRequest) {
        String authHeader = httpServletRequest.getParameter(this.tokenHttpParamKey);
        if (StringUtils.isNotBlank(authHeader)) {
            return authHeader;
        }
        return null;
    }

    private String getTokenFromHttpHead(HttpServletRequest httpServletRequest) {
        String authHeader = httpServletRequest.getHeader(this.tokenHeader);
        if (authHeader != null && authHeader.startsWith(tokenHead)) {
            return authHeader.substring(tokenHead.length());
        }
        return null;
    }
}
